<script setup lang="ts">
import {
  Check,
  Clock,
  Document,
  Download,
  Plus,
  Refresh,
  Search,
  Star,
  Tools,
  Upload,
} from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import { computed, onMounted, reactive, ref } from 'vue'
import AppointmentFormDialog from '../components/AppointmentFormDialog.vue'
import LogsDialog from '../components/LogsDialog.vue'
import PlaceholderManagementDialog from '../components/PlaceholderManagementDialog.vue'

// 数据状态
const loading = ref(false)
const appointments = ref<any[]>([])
const selectedRows = ref<any[]>([])
const statistics = ref<any>({})
const searchText = ref('')

// 分页
const pagination = reactive({
  page: 1,
  page_size: 20,
  total: 0,
})

// 过滤器
const filters = reactive({
  appointment_status: [],
  issuing_country: [],
  is_placeholder: undefined,
  is_monitor: undefined,
  priority: [],
  appointment_date_range: { start: '', end: '' },
  date_range: { start: '', end: '' },
  booked_at_range: { start: '', end: '' },
  search: '',
})

// 高级筛选开关
const showAdvancedFilters = ref(false)

// 为日期范围选择器提供 v-model 的计算属性代理（Element Plus 需要数组/Null）
const appointmentDateRange = computed({
  get() {
    const start = filters.appointment_date_range?.start
    const end = filters.appointment_date_range?.end
    if (start && end)
      return [start, end]
    return null
  },
  set(val) {
    const arr = Array.isArray(val) ? val : []
    const start = arr[0] || ''
    const end = arr[1] || ''
    filters.appointment_date_range.start = start
    filters.appointment_date_range.end = end
  },
})

// 预约成功时间（日期时间范围）
const bookedAtRange = computed({
  get() {
    const start = filters.booked_at_range?.start
    const end = filters.booked_at_range?.end
    if (start && end)
      return [start, end]
    return null
  },
  set(val) {
    const arr = Array.isArray(val) ? val : []
    const start = arr[0] || ''
    const end = arr[1] || ''
    filters.booked_at_range.start = start
    filters.booked_at_range.end = end
  },
})

// 创建时间（范围）
const createdAtRange = computed({
  get() {
    const start = (filters as any).date_range?.start
    const end = (filters as any).date_range?.end
    if (start && end)
      return [start, end]
    return null
  },
  set(val) {
    const arr = Array.isArray(val) ? val : []
    const start = arr[0] || ''
    const end = arr[1] || ''
    ;(filters as any).date_range = { start, end }
  },
})

// 快速选择/快捷项
// 已使用 shortcuts 提供快捷选择，不再需要 formatYMD（保留结构如需扩展快捷文案时可恢复）

// 日期快捷项：预约日期（统一：今天/近7天/近30天）
const appointmentDateShortcuts = [
  {
    text: '今天',
    value: () => {
      const start = new Date()
      const end = new Date()
      return [start, end]
    },
  },
  {
    text: '近7天',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setDate(start.getDate() - 6)
      return [start, end]
    },
  },
  {
    text: '近30天',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setDate(start.getDate() - 29)
      return [start, end]
    },
  },
  {
    text: '清空',
    onClick: (picker: any) => picker?.emit?.('pick', []),
  },
]

// 日期时间快捷项：预约成功时间（支持日期时间范围选择）
const bookedAtShortcuts = [
  {
    text: '今天',
    value: () => {
      const now = new Date()
      const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
      return [start, end]
    },
  },
  {
    text: '近7天',
    value: () => {
      const now = new Date()
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
      const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6, 0, 0, 0)
      return [start, end]
    },
  },
  {
    text: '近30天',
    value: () => {
      const now = new Date()
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
      const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 29, 0, 0, 0)
      return [start, end]
    },
  },
  {
    text: '本周',
    value: () => {
      const now = new Date()
      const day = now.getDay()
      const diff = now.getDate() - day + (day === 0 ? -6 : 1) // 调整为周一
      const start = new Date(now.getFullYear(), now.getMonth(), diff, 0, 0, 0)
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
      return [start, end]
    },
  },
  {
    text: '本月',
    value: () => {
      const now = new Date()
      const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0)
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
      return [start, end]
    },
  },
  {
    text: '最近1小时',
    value: () => {
      const now = new Date()
      const start = new Date(now.getTime() - 60 * 60 * 1000)
      return [start, now]
    },
  },
  {
    text: '最近24小时',
    value: () => {
      const now = new Date()
      const start = new Date(now.getTime() - 24 * 60 * 60 * 1000)
      return [start, now]
    },
  },
  {
    text: '清空',
    onClick: (picker: any) => picker?.emit?.('pick', []),
  },
]

// 日期快捷项：创建时间（统一：今天/近7天/近30天）
const createdAtShortcuts = [
  {
    text: '今天',
    value: () => {
      const start = new Date()
      const end = new Date()
      return [start, end]
    },
  },
  {
    text: '近7天',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setDate(start.getDate() - 6)
      return [start, end]
    },
  },
  {
    text: '近30天',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setDate(start.getDate() - 29)
      return [start, end]
    },
  },
  {
    text: '清空',
    onClick: (picker: any) => picker?.emit?.('pick', []),
  },
]

// 排序
const sortInfo = reactive({
  sort_field: 'created_at',
  sort_order: 'desc',
})

// 对话框状态
const showCreateDialog = ref(false)
const showLogsDialog = ref(false)
const showPlaceholderDialog = ref(false)
const generatingPlaceholders = ref(false)
const editingAppointment = ref(null)
const currentAppointmentId = ref(0)

// 修复数据对话框状态
const repairDialog = reactive({
  visible: false,
  running: false,
  result: null as { repaired: number, skipped: number, failed: number, total: number, message: string } | null,
})

// 是否开发环境
const isDev = import.meta.env.DEV

// 导入结果详情对话框
const importDialog = reactive({
  visible: false,
  errors: [] as Array<{ row: number, message: string, data?: any }>,
})

// 选中项中可取消的“唯一参考号”数量（有有效参考号且状态非 cancelled）
const selectedCancellableCount = computed(() => {
  const refs = selectedRows.value
    .filter((r: any) => r?.appointment_status !== 'cancelled')
    .map((r: any) => String(r?.reference_number || '').slice(-6))
    .filter((v: string) => v && v.length === 6)
  return Array.from(new Set(refs)).length
})

// 批量删除选中
async function batchDeleteSelected() {
  if (selectedRows.value.length === 0) {
    ElMessage.warning('请先选择要删除的记录')
    return
  }
  try {
    await ElMessageBox.confirm(
      `确定要删除选中的 ${selectedRows.value.length} 条记录吗？此操作不可恢复。`,
      '批量删除确认',
      {
        confirmButtonText: '删除',
        cancelButtonText: '取消',
        type: 'warning',
      },
    )

    const ids = selectedRows.value.map(r => r.id)
    const res = await window.api.appointment.batchOperation({
      appointment_ids: ids,
      operation: 'delete',
    })
    const ok = Number(res?.data?.success_count || 0)
    const fail = Number(res?.data?.failed_count || 0)
    if (res.success) {
      ElMessage.success(`批量删除完成：成功 ${ok} 条，失败 ${fail} 条`)
    }
    else {
      ElMessage.error(`部分失败：成功 ${ok} 条，失败 ${fail} 条`)
    }
    await loadAppointments()
    await loadStatistics()
  }
  catch (e) {
    if (e !== 'cancel')
      ElMessage.error(String(e))
  }
}

// 远程请求（网站）相关
const remote = reactive({
  refLast6: '',
  officeCode: 'HKLO',
  preferredDates: '' as string, // 逗号分隔 YYYY-MM-DD
  preferredTimes: '' as string, // 逗号分隔 HH:MM 或 HHMM
  running: false,
})

// 批量取消运行中状态
const batchCancelRunning = ref(false)

// 行内取消按钮 loading 状态
const rowCancelling: Record<number, boolean> = reactive({})

// UI 控制：后台取消
const ui = reactive({
  backgroundCancel: false,
})

// 批量取消进行中参考号集合（按后6位）
const batchRefsCancelling: Record<string, boolean> = reactive({})

// 查询对话框
const queryDialog = reactive({
  visible: false,
  method: 'ref' as 'ref' | 'id',
  refLast6: '',
  idQuery: {
    identity: '', // 身份证/护照合并输入
    license: '',
  },
  running: false,
  results: [] as Array<{ ref?: string, success: boolean, data?: any, error?: string }>,
})

// 更改预约表单对话框（选中模式）
const updateDialog = reactive({
  visible: false,
  officeCode: 'HKLO',
  preferredDates: '' as string,
  preferredTimes: '' as string,
  running: false,
  results: [] as Array<{ ref: string, success: boolean, referenceNumber?: string, error?: string }>,
})

// 当前查询结果（单条显示）
const currentQueryResult = computed(() => (queryDialog.results && queryDialog.results.length ? queryDialog.results[0] : null))

async function writebackFromQueryRow(row: any) {
  try {
    if (!row?.data?.parsed) {
      ElMessage.warning('当前行无可写回的数据')
      return
    }
    if (!selectedRows.value.length) {
      ElMessage.warning('请先在主表勾选要写回的记录')
      return
    }

    const parsed = row.data.parsed
    const updates: Record<string, any> = {}
    if (parsed.reference_number)
      updates.reference_number = parsed.reference_number
    // 预约日期：统一写回 YYYY-MM-DD（解析结果若为 DD-MM-YYYY 则转换）
    const toYMD = (dmy?: string, ymd?: string) => {
      if (ymd && /^\d{4}-\d{2}-\d{2}$/.test(ymd))
        return ymd
      if (dmy && /^\d{2}-\d{2}-\d{4}$/.test(dmy)) {
        const [dd, mm, yyyy] = dmy.split('-')
        return `${yyyy}-${mm}-${dd}`
      }
      return ymd || dmy || ''
    }
    const dateYMD = toYMD(parsed.appointment_date_dmy, parsed.appointment_date)
    if (dateYMD)
      updates.appointment_date = dateYMD
    if (parsed.appointment_time)
      updates.appointment_time = parsed.appointment_time

    // 若解析到参考号，则将状态一并标记为已预约（后端会自动补齐 booked_at）
    if (parsed.reference_number)
      updates.appointment_status = 'booked'

    // 不写回任何脱敏的个人信息（姓名/证件/电话/邮箱等）

    // 计算匹配目标：按参考号后6位匹配选中行；若行中无ref则退化为全部选中
    const targetRef6 = String(row.ref || '').trim()
    const targets = selectedRows.value.filter((r: any) => {
      const rRef6 = String(r.reference_number || '').slice(-6)
      return targetRef6 ? rRef6 === targetRef6 : true
    })
    if (!targets.length) {
      ElMessage.warning('选中记录中未找到与该结果匹配的参考号后6位')
      return
    }

    let ok = 0
    for (const t of targets) {
      const res = await window.api.appointment.update(t.id, updates)
      if (res?.success)
        ok++
    }
    if (ok > 0) {
      ElMessage.success(`写回完成：成功 ${ok} 条`)
      await loadAppointments()
    }
    else {
      ElMessage.error('写回失败')
    }
  }
  catch (e) {
    ElMessage.error(String(e))
  }
}

async function cancelFromQueryRow(row: any) {
  try {
    // 参考后6位优先：从行ref或解析到的reference_number中取
    let ref6 = String(row?.ref || '').trim()
    if (!ref6 && row?.data?.parsed?.reference_number)
      ref6 = String(row.data.parsed.reference_number).slice(-6)
    if (!ref6 || ref6.length !== 6) {
      ElMessage.warning('未找到可用于取消的参考号后6位')
      return
    }
    // 二次确认
    await ElMessageBox.confirm(`确认取消参考号后6位为 ${ref6} 的预约吗？操作不可撤销。`, '确认取消', { type: 'warning' })

    queryDialog.running = true
    const hasApi = (window as any)?.api?.remote?.cancelByRef
    const res = hasApi
      ? await (window as any).api.remote.cancelByRef(ref6)
      : await (window as any).electron?.ipcRenderer?.invoke('remote-appointment:cancel-by-ref', ref6)

    if (res?.success) {
      ElMessage.success(res?.message || '取消成功')
      // 本地标记为取消：仅更新匹配参考号后6位的选中记录，不清空预约日期/时间
      const targets = selectedRows.value.filter((r: any) => String(r.reference_number || '').slice(-6) === ref6)
      for (const t of targets) {
        try {
          await window.api.appointment.update(t.id, { appointment_status: 'cancelled' })
        }
        catch (e) {
          console.warn('本地状态更新失败', t.id, e)
        }
      }
      await loadAppointments()
    }
    else {
      ElMessage.error(res?.error || '取消失败')
    }
  }
  catch (e) {
    if (e !== 'cancel')
      ElMessage.error(String(e))
  }
  finally {
    queryDialog.running = false
  }
}

function fillQueryFromSelected() {
  if (!selectedRows.value.length)
    return
  const first = selectedRows.value[0]
  // 参考号后6位
  queryDialog.refLast6 = String(first.reference_number || '').slice(-6)
  // 身份证/护照
  queryDialog.idQuery.identity = String(first.passport_number || '').toUpperCase().trim()
  // 驾照号码
  queryDialog.idQuery.license = String(first.license_number || '').toUpperCase().trim()
}

// 修复缺失的预约数据
async function handleRepairData() {
  try {
    repairDialog.running = true
    repairDialog.result = null

    const res = await window.api.booking.repairMissingData()

    if (res?.success) {
      repairDialog.result = {
        repaired: res.repaired || 0,
        skipped: res.skipped || 0,
        failed: res.failed || 0,
        total: res.total || 0,
        message: res.message || '修复完成',
      }
      ElMessage.success(res.message || '修复完成')
      // 刷新数据
      await loadAppointments()
      await loadStatistics()
    }
    else {
      ElMessage.error(res?.error || '修复失败')
      repairDialog.result = {
        repaired: res?.repaired || 0,
        skipped: res?.skipped || 0,
        failed: res?.failed || 0,
        total: res?.total || 0,
        message: res?.error || res?.message || '修复失败',
      }
    }
  }
  catch (e: any) {
    ElMessage.error(e?.message || String(e))
    repairDialog.result = {
      repaired: 0,
      skipped: 0,
      failed: 0,
      total: 0,
      message: e?.message || String(e),
    }
  }
  finally {
    repairDialog.running = false
  }
}

function openQueryDialog() {
  queryDialog.visible = true
  queryDialog.method = 'ref'
  queryDialog.refLast6 = remote.refLast6 || ''
  // 如果有选中记录，作为辅助填充
  if (selectedRows.value.length)
    fillQueryFromSelected()
  queryDialog.results = []
}

// openCancelDialog 已移除，直接使用统一查询弹窗

// 已整合：openUpdateDialog 不再使用

function getSelectedRefLast6List(): string[] {
  return selectedRows.value
    .map(r => String(r.reference_number || '').slice(-6))
    .filter(v => v && v.length === 6)
}

// 仅选取“可取消”的参考号后6位：排除已取消状态及无有效参考号的记录
function getSelectedCancellableRefs(): string[] {
  const refs = selectedRows.value
    .filter((r: any) => r.appointment_status !== 'cancelled')
    .map((r: any) => String(r.reference_number || '').slice(-6))
    .filter((v: string) => v && v.length === 6)
  return Array.from(new Set(refs))
}

// 直接取消（单条，不经查询）
async function cancelAppointment(appointment: any) {
  try {
    const ref6 = String(appointment?.reference_number || '').slice(-6)
    if (!ref6 || ref6.length !== 6) {
      ElMessage.warning('该记录缺少有效参考号，无法取消')
      return
    }
    await ElMessageBox.confirm(`确认取消参考号后6位为 ${ref6} 的预约吗？操作不可撤销。`, '确认取消', { type: 'warning' })

    rowCancelling[appointment.id] = true
    const notify = ElNotification({ title: '正在取消', message: `参考后6位 ${ref6} 正在取消中…`, type: 'info', duration: 0 })

    const run = async () => {
      const res = await (window as any).api.remote.cancelByRef(ref6)
      notify?.close?.()
      if (res?.success) {
        ElNotification({ title: '取消成功', message: res?.message || `已取消 ${ref6}`, type: 'success' })
        try { await window.api.appointment.update(appointment.id, { appointment_status: 'cancelled' }) }
        catch (e) { console.warn('本地状态更新失败', appointment.id, e) }
        await loadAppointments()
        await loadStatistics()
      }
      else {
        ElNotification({ title: '取消失败', message: res?.error || `取消失败 ${ref6}`, type: 'error' })
      }
    }

    if (ui.backgroundCancel) {
      // 后台运行，不阻塞交互
      void run().finally(() => { rowCancelling[appointment.id] = false })
    }
    else {
      await run()
      rowCancelling[appointment.id] = false
    }
  }
  catch (e) {
    if (e !== 'cancel')
      ElMessage.error(String(e))
  }
}

// 批量取消（选中记录，直达远程取消）
async function batchCancelSelected() {
  if (selectedRows.value.length === 0) {
    ElMessage.warning('请先选择要取消的记录')
    return
  }
  const refs = getSelectedCancellableRefs()
  if (!refs.length) {
    ElMessage.warning('选中记录均不可取消（已取消或无参考号）')
    return
  }
  try {
    const skipped = selectedRows.value.length - refs.length
    const msg = skipped > 0
      ? `将取消 ${refs.length} 条，跳过 ${skipped} 条（已取消、无参考号或重复）。此操作将直接在网站上取消，且不可恢复。`
      : `确定要取消选中的 ${refs.length} 条预约吗？此操作将直接在网站上取消，且不可恢复。`
    await ElMessageBox.confirm(
      msg,
      '批量取消确认',
      {
        confirmButtonText: '取消预约',
        cancelButtonText: '返回',
        type: 'warning',
      },
    )

    // 标记这些参考号为进行中
    for (const r of refs) batchRefsCancelling[r] = true

    const start = async () => {
      const notify = ElNotification({ title: '正在批量取消', message: `共 ${refs.length} 条正在取消中…`, type: 'info', duration: 0 })
      const res = await (window as any).api.remote.batchCancel(refs, { concurrency: 2, retries: 3, retryDelayMs: 600, verify: true, verifyDelayMs: 600 })
      notify?.close?.()
      const ok = Number(res?.ok || 0)
      const fail = Number(res?.fail || 0)
      if (res?.success) {
        ElNotification({ title: '批量取消完成', message: `成功 ${ok} 条，失败 ${fail} 条`, type: fail ? 'warning' : 'success' })
      }
      else {
        ElNotification({ title: '批量取消部分失败', message: `成功 ${ok} 条，失败 ${fail} 条`, type: 'warning' })
      }

      // 如果有 warns，提示远端延迟或验证异常
      if (Array.isArray(res?.warns) && res.warns.length) {
        const tips = res.warns.slice(0, 3).map((w: any) => w?.ref).filter(Boolean).join(', ')
        ElNotification({ title: '取消完成但存在延迟', message: `部分记录验证仍可查询（可能延迟）：${tips} 等`, type: 'info' })
      }

      // 本地状态同步（仅对成功的参考号后6位）
      const failedSet = new Set<string>((res?.errors || []).map((e: any) => String(e?.ref || '')))
      const successRefs = refs.filter(r => r && !failedSet.has(String(r)))
      if (successRefs.length) {
        const successSet = new Set(successRefs)
        const targets = selectedRows.value.filter((r: any) => successSet.has(String(r.reference_number || '').slice(-6)))
        for (const t of targets) {
          try { await window.api.appointment.update(t.id, { appointment_status: 'cancelled' }) }
          catch (e) { console.warn('本地状态更新失败', t.id, e) }
        }
      }

      await loadAppointments()
      await loadStatistics()
    }

    if (ui.backgroundCancel) {
      // 后台取消：不阻塞按钮 loading，交给通知提示
      void start().finally(() => { for (const r of refs) delete batchRefsCancelling[r] })
    }
    else {
      batchCancelRunning.value = true
      await start()
      for (const r of refs) delete batchRefsCancelling[r]
    }
  }
  catch (e) {
    if (e !== 'cancel')
      ElMessage.error(String(e))
  }
  finally {
    if (!ui.backgroundCancel)
      batchCancelRunning.value = false
  }
}

async function submitQueryDialog() {
  // 三种模式：input（手填后6位）、selected（从选中行取后6位）、id（按证件/执照查询）
  if (queryDialog.method === 'id') {
    const identity = (queryDialog.idQuery.identity || '').trim()
    const license = (queryDialog.idQuery.license || '').trim()
    if (!license || !identity) {
      ElMessage.warning('按证件/执照查询需同时提供：(身份证或护照) + 驾照号码')
      return
    }
  }
  const refs = queryDialog.method === 'ref'
    ? (queryDialog.refLast6 && queryDialog.refLast6.length === 6 ? [queryDialog.refLast6] : [])
    : []
  if (queryDialog.method !== 'id' && !refs.length) {
    ElMessage.warning('没有可查询的参考号后6位')
    return
  }
  queryDialog.running = true
  queryDialog.results = []
  try {
    if (queryDialog.method === 'id') {
      const identity = (queryDialog.idQuery.identity || '').toUpperCase().trim()
      const license = (queryDialog.idQuery.license || '').toUpperCase().trim()
      const cleaned = identity.replace(/[\s()]/g, '')
      // 简单判定HKID：1-2字母 + 6数字 + 校验位(0-9或A)
      const isHKID = /^[A-Z]{1,2}\d{6}[0-9A]$/.test(cleaned)
      const payload: any = { license }
      if (isHKID)
        payload.hkid = cleaned
      else
        payload.passport = identity
      const res = await (window as any).api.remote.queryById(payload)
      queryDialog.results.push({ success: !!res?.success, data: res?.data, error: res?.error })
    }
    else {
      for (const ref6 of refs) {
        const hasApi = (window as any)?.api?.remote?.queryByRef
        const res = hasApi
          ? await (window as any).api.remote.queryByRef(ref6)
          : await (window as any).electron?.ipcRenderer?.invoke('remote-appointment:query-by-ref', ref6)
        // 命中“未找到记录”错误：不展示行，由对话框内的“无结果”占位体现
        const noRec = res && res.success === false && /未找到|沒有紀錄|没有记录|NO_RECORD/i.test(String(res?.error || ''))
        if (noRec)
          continue
        queryDialog.results.push({ ref: ref6, success: !!res?.success, data: res?.data, error: res?.error })
      }
      if (queryDialog.results.length === 0) {
        const msgRefs = refs.join(', ')
        ElMessage.info(msgRefs ? `参考号后6位 ${msgRefs} 未查询到记录` : '未查询到记录')
      }
    }
  }
  catch (e) {
    ElMessage.error(String(e))
  }
  finally {
    queryDialog.running = false
  }
}

// 旧 submitCancelDialog 已合并到查询弹窗操作中

async function submitUpdateDialog() {
  const refs = getSelectedRefLast6List()
  if (!refs.length) {
    ElMessage.warning('选中记录没有有效参考号')
    return
  }
  updateDialog.running = true
  updateDialog.results = []
  try {
    for (const ref6 of refs) {
      // 禁止对已取消/过期记录发起“更改”以避免远端返回不可控错误
      const target = selectedRows.value.find((r: any) => String(r.reference_number || '').slice(-6) === ref6)
      if (target && (target.appointment_status === 'cancelled')) {
        updateDialog.results.push({ ref: ref6, success: false, error: '该记录已取消，不能更改时段' })
        continue
      }
      const payload = {
        refLast6: ref6,
        officeCode: updateDialog.officeCode || 'HKLO',
        preferredDates: updateDialog.preferredDates.split(',').map(s => s.trim()).filter(Boolean),
        preferredTimes: updateDialog.preferredTimes.split(',').map(s => s.trim()).filter(Boolean),
      }
      const hasApi = (window as any)?.api?.remote?.updateTimeslot
      const r = hasApi
        ? await (window as any).api.remote.updateTimeslot(payload)
        : await (window as any).electron?.ipcRenderer?.invoke('remote-appointment:update-timeslot', payload)
      updateDialog.results.push({ ref: ref6, success: !!r?.success, referenceNumber: r?.referenceNumber, error: r?.error })
    }
    loadAppointments()
    loadStatistics()
  }
  catch (e) {
    ElMessage.error(String(e))
  }
  finally {
    updateDialog.running = false
  }
}

// 加载预约列表
async function loadAppointments() {
  loading.value = true
  try {
    // 仅传递可克隆的纯对象，避免 Proxy/Ref 导致结构化复制失败
    const plainFilters = (() => {
      try {
        // 某些构建环境下 structuredClone 可能未声明类型，但运行时可用
        return (globalThis as any).structuredClone
          ? (globalThis as any).structuredClone(filters)
          : JSON.parse(JSON.stringify(filters))
      }
      catch {
        try { return JSON.parse(JSON.stringify(filters)) }
        catch { return {} as any }
      }
    })()
    // 防御性清洗：状态/优先级去重并约束到允许集合，避免异常值触发主进程报错
    if (Array.isArray(plainFilters.appointment_status)) {
      const allowed = new Set(['pending', 'booked', 'confirmed', 'cancelled', 'failed'])
      plainFilters.appointment_status = Array.from(new Set(
        plainFilters.appointment_status.filter((s: any) => allowed.has(String(s))),
      ))
    }
    if (Array.isArray(plainFilters.priority)) {
      const allowedP = new Set([1, 2, 3, 4, 5])
      plainFilters.priority = Array.from(new Set(
        plainFilters.priority.map((n: any) => Number(n)).filter((n: number) => allowedP.has(n)),
      ))
    }
    const plainPagination = { page: pagination.page, page_size: pagination.page_size }
    const plainSort = { sort_field: sortInfo.sort_field, sort_order: sortInfo.sort_order }

    const response = await window.api.appointment.list(plainFilters, {
      ...plainPagination,
      ...plainSort,
    })

    if (response.success) {
      appointments.value = response.data.appointments
      pagination.total = response.data.total
    }
    else {
      ElMessage.error(response.error || '加载预约列表失败')
    }
  }
  catch (error) {
    console.error('加载预约列表失败:', error)
    ElMessage.error('加载预约列表失败')
  }
  finally {
    loading.value = false
  }
}

// 加载统计信息
async function loadStatistics() {
  try {
    const response = await window.api.appointment.statistics()
    if (response.success) {
      statistics.value = response.data
    }
  }
  catch (error) {
    console.error('加载统计信息失败:', error)
  }
}

// 搜索处理
function handleSearch() {
  filters.search = searchText.value
  pagination.page = 1
  loadAppointments()
}

// 应用过滤器
function applyFilters() {
  pagination.page = 1
  loadAppointments()
}

// 重置过滤器
function resetFilters() {
  Object.assign(filters, {
    appointment_status: [],
    issuing_country: [],
    is_placeholder: undefined,
    is_monitor: undefined,
    priority: [],
    appointment_date_range: { start: '', end: '' },
    date_range: { start: '', end: '' },
    booked_at_range: { start: '', end: '' },
    search: '',
  })
  searchText.value = ''
  pagination.page = 1
  loadAppointments()
}

// 选择变化
function handleSelectionChange(selection) {
  selectedRows.value = selection
  // 只要有选中就自动填充（当查询对话框开启时）
  if (queryDialog.visible && selectedRows.value.length)
    fillQueryFromSelected()
}

// 排序变化
function handleSortChange({ prop, order }) {
  sortInfo.sort_field = prop
  sortInfo.sort_order = order === 'ascending' ? 'asc' : 'desc'
  loadAppointments()
}

// 编辑预约
function editAppointment(appointment) {
  editingAppointment.value = { ...appointment }
  showCreateDialog.value = true
}

// 删除预约
async function deleteAppointment(appointment) {
  try {
    await ElMessageBox.confirm(
      `确定要删除预约记录 "${appointment.sequence_number}" 吗？`,
      '确认删除',
      {
        confirmButtonText: '删除',
        cancelButtonText: '取消',
        type: 'warning',
      },
    )

    const response = await window.api.appointment.delete(appointment.id)
    if (response.success) {
      ElMessage.success('删除成功')
      loadAppointments()
      loadStatistics()
    }
    else {
      ElMessage.error(response.error || '删除失败')
    }
  }
  catch (error) {
    if (error !== 'cancel') {
      console.error('删除预约失败:', error)
      ElMessage.error('删除失败')
    }
  }
}

// 打开占号管理对话框
function openPlaceholderDialog() {
  showPlaceholderDialog.value = true
}

// 占号对话框成功回调
function onPlaceholderSuccess() {
  loadAppointments()
  loadStatistics()
}

// 占号生成状态变化回调
function onPlaceholderGeneratingChange(generating: boolean) {
  generatingPlaceholders.value = generating
}

// 查看日志
function viewLogs(appointment) {
  currentAppointmentId.value = appointment.id
  showLogsDialog.value = true
}

// 表单成功处理
function handleFormSuccess() {
  loadAppointments()
  loadStatistics()
  editingAppointment.value = null
}

// Excel 导入
async function importExcel() {
  try {
    const result = await window.api.dialog.openFile({
      title: '选择Excel文件',
      filters: [
        { name: 'Excel文件', extensions: ['xlsx', 'xls'] },
      ],
    })

    if (!result.data.canceled && result.data.filePaths.length > 0) {
      const filePath = result.data.filePaths[0]
      loading.value = true

      const response = await window.api.excel.import(filePath)
      if (response.success) {
        const importResult = response.data
        // 结果提示
        if (importResult.error_count > 0) {
          ElMessage.warning(`导入完成：成功 ${importResult.success_count} 条，失败 ${importResult.error_count} 条`)
        }
        else {
          ElMessage.success(`导入完成：成功 ${importResult.success_count} 条，失败 0 条`)
        }

        // 若有失败，弹出详情对话框
        if (Array.isArray(importResult.errors) && importResult.errors.length) {
          importDialog.errors = importResult.errors.map((e: any) => ({ row: e.row, message: e.message, data: e.data }))
          importDialog.visible = true
        }

        loadAppointments()
        loadStatistics()
      }
      else {
        ElMessage.error(response.error || '导入失败')
      }
    }
  }
  catch (error) {
    console.error('Excel导入失败:', error)
    ElMessage.error('导入失败')
  }
  finally {
    loading.value = false
  }
}

// 下载模板
async function downloadTemplate() {
  try {
    const response = await window.api.excel.generateTemplate()
    if (response.success) {
      // 创建下载链接
      const blob = new Blob([response.data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = '预约数据导入模板.xlsx'
      a.click()
      URL.revokeObjectURL(url)
      ElMessage.success('模板下载成功')
    }
    else {
      ElMessage.error(response.error || '下载模板失败')
    }
  }
  catch (error) {
    console.error('下载模板失败:', error)
    ElMessage.error('下载模板失败')
  }
}

// 导出Excel
async function exportExcel() {
  try {
    const appointmentIds = selectedRows.value.map(row => row.id)
    const response = await window.api.excel.export(appointmentIds)

    if (response.success) {
      // 创建下载链接
      const blob = new Blob([response.data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `预约数据_${new Date().toISOString().slice(0, 10)}.xlsx`
      a.click()
      URL.revokeObjectURL(url)
      ElMessage.success('导出成功')
    }
    else {
      ElMessage.error(response.error || '导出失败')
    }
  }
  catch (error) {
    console.error('导出Excel失败:', error)
    ElMessage.error('导出失败')
  }
}

// 导出全部 Excel（不受当前分页/选择限制）
async function exportAllExcel() {
  try {
    const response = await window.api.excel.export()

    if (response.success) {
      const blob = new Blob([response.data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `预约数据_全部_${new Date().toISOString().slice(0, 10)}.xlsx`
      a.click()
      URL.revokeObjectURL(url)
      ElMessage.success('导出成功')
    }
    else {
      ElMessage.error(response.error || '导出失败')
    }
  }
  catch (error) {
    console.error('导出全部Excel失败:', error)
    ElMessage.error('导出失败')
  }
}

// 按当前筛选导出全部
async function exportFilteredExcel() {
  try {
    const plainFilters = JSON.parse(JSON.stringify(filters))
    const response = await window.api.excel.exportByFilters(plainFilters)
    if (response.success) {
      // 生成包含筛选条件的文件名
      const parts: string[] = []
      if (Array.isArray(plainFilters.appointment_status) && plainFilters.appointment_status.length) {
        const statusText = plainFilters.appointment_status.map((s: string) => getStatusText(s)).join('+')
        parts.push(`状态-${statusText}`)
      }
      if (Array.isArray(plainFilters.issuing_country) && plainFilters.issuing_country.length) {
        const countryText = plainFilters.issuing_country.map((c: string) => (c === 'CN' ? '中国内地' : '海外')).join('+')
        parts.push(`签发-${countryText}`)
      }
      if (typeof plainFilters.is_placeholder === 'boolean') {
        parts.push(`类型-${plainFilters.is_placeholder ? '占号' : '真实'}`)
      }
      if (Array.isArray(plainFilters.priority) && plainFilters.priority.length) {
        parts.push(`优先-${plainFilters.priority.join('+')}级`)
      }
      if (plainFilters.appointment_date_range?.start && plainFilters.appointment_date_range?.end) {
        parts.push(`预约日期-${plainFilters.appointment_date_range.start}至${plainFilters.appointment_date_range.end}`)
      }
      if (plainFilters.date_range?.start && plainFilters.date_range?.end) {
        parts.push(`创建时间-${plainFilters.date_range.start}至${plainFilters.date_range.end}`)
      }
      if (plainFilters.booked_at_range?.start && plainFilters.booked_at_range?.end) {
        // 格式化时间显示：如果包含时间部分，简化显示
        const startStr = String(plainFilters.booked_at_range.start)
        const endStr = String(plainFilters.booked_at_range.end)
        const startDisplay = startStr.includes(' ') ? startStr.replace(/\s+/g, '') : startStr
        const endDisplay = endStr.includes(' ') ? endStr.replace(/\s+/g, '') : endStr
        parts.push(`预约成功时间-${startDisplay}至${endDisplay}`)
      }
      if (plainFilters.search && String(plainFilters.search).trim()) {
        const kw = String(plainFilters.search).trim()
        parts.push(`关键词-${kw}`)
      }
      const rawSuffix = parts.join('_') || '全部'
      const safeSuffix = rawSuffix
        .replace(/[\\/:*?"<>|]/g, '')
        .replace(/\s+/g, '')
        .slice(0, 180)

      const blob = new Blob([response.data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `预约数据_按筛选_${safeSuffix}_${new Date().toISOString().slice(0, 10)}.xlsx`
      a.click()
      URL.revokeObjectURL(url)
      ElMessage.success('导出成功')
    }
    else {
      ElMessage.error(response.error || '导出失败')
    }
  }
  catch (error) {
    console.error('按筛选导出Excel失败:', error)
    ElMessage.error('导出失败')
  }
}

// 工具函数
function getPriorityType(priority) {
  const types = { 1: 'danger', 2: 'warning', 3: '', 4: 'info', 5: 'success' }
  return types[priority] || ''
}

function getStatusType(status) {
  const types = {
    pending: 'warning',
    booked: 'success',
    confirmed: 'success',
    cancelled: 'info',
    failed: 'danger',
  }
  return types[status] || ''
}

function getStatusText(status) {
  const texts = {
    pending: '待处理',
    booked: '已预约',
    confirmed: '已确认',
    cancelled: '已取消',
    failed: '失败',
  }
  return texts[status] || status
}

function formatAppointmentDate(val) {
  if (!val)
    return ''
  const s = String(val)
  if (/^\d{2}-\d{2}-\d{4}$/.test(s)) {
    const [dd, mm, yyyy] = s.split('-')
    return `${yyyy}-${mm}-${dd}`
  }
  return s
}

function formatDateTime(dateStr) {
  if (!dateStr)
    return ''
  const d = dayjs(dateStr)
  return d.isValid() ? d.format('YYYY-MM-DD HH:mm:ss') : ''
}

// 初始化
onMounted(() => {
  loadAppointments()
  loadStatistics()
})
</script>

<template>
  <div class="appointment-page">
    <!-- 页面标题 -->
    <div class="page-header">
      <h2 class="text-2xl text-gray-800 font-bold">
        预约管理
      </h2>
      <p class="mt-1 text-gray-600">
        管理所有预约记录，支持增删改查、Excel导入导出等操作
      </p>
    </div>

    <!-- 统计卡片 -->
    <div class="stats-grid">
      <div class="stat-card">
        <div class="stat-icon bg-blue-100 text-blue-600">
          <el-icon><Document /></el-icon>
        </div>
        <div class="stat-content">
          <h3 class="stat-number">
            {{ statistics.total_count || 0 }}
          </h3>
          <p class="stat-label">
            总记录数
          </p>
        </div>
      </div>

      <div class="stat-card">
        <div class="stat-icon bg-green-100 text-green-600">
          <el-icon><Check /></el-icon>
        </div>
        <div class="stat-content">
          <h3 class="stat-number">
            {{ statistics.booked_count || 0 }}
          </h3>
          <p class="stat-label">
            预约成功
          </p>
        </div>
      </div>

      <div class="stat-card">
        <div class="stat-icon bg-yellow-100 text-yellow-600">
          <el-icon><Clock /></el-icon>
        </div>
        <div class="stat-content">
          <h3 class="stat-number">
            {{ statistics.pending_count || 0 }}
          </h3>
          <p class="stat-label">
            待处理
          </p>
        </div>
      </div>

      <div class="stat-card">
        <div class="stat-icon bg-purple-100 text-purple-600">
          <el-icon><Star /></el-icon>
        </div>
        <div class="stat-content">
          <h3 class="stat-number">
            {{ statistics.placeholder_count || 0 }}
          </h3>
          <p class="stat-label">
            占号数据
          </p>
        </div>
      </div>
    </div>

    <!-- 操作工具栏 -->
    <div class="toolbar">
      <div class="toolbar-left">
        <el-button type="primary" @click="showCreateDialog = true">
          <el-icon><Plus /></el-icon>
          新建预约
        </el-button>

        <el-button @click="importExcel">
          <el-icon><Upload /></el-icon>
          导入Excel
        </el-button>

        <el-button @click="downloadTemplate">
          <el-icon><Download /></el-icon>
          下载模板
        </el-button>

        <el-button @click="exportFilteredExcel">
          <el-icon><Download /></el-icon>
          按筛选导出
        </el-button>

        <el-button @click="exportAllExcel">
          <el-icon><Download /></el-icon>
          导出全部
        </el-button>

        <el-button :disabled="selectedRows.length === 0" @click="exportExcel">
          <el-icon><Download /></el-icon>
          导出选中
        </el-button>

        <el-button type="danger" :disabled="selectedCancellableCount === 0" :loading="batchCancelRunning" @click="batchCancelSelected">
          批量取消预约
        </el-button>

        <el-button type="primary" :loading="generatingPlaceholders" @click="openPlaceholderDialog">
          <el-icon><Star /></el-icon>
          生成占号
        </el-button>

        <el-button type="danger" :disabled="selectedRows.length === 0" @click="batchDeleteSelected">
          批量删除
        </el-button>

        <!-- 高级功能按钮（仅开发环境） -->
        <el-button v-if="isDev" type="warning" @click="repairDialog.visible = true">
          <el-icon><Tools /></el-icon>
          修复数据
        </el-button>
      </div>

      <div class="toolbar-right">
        <el-input
          v-model="searchText"
          placeholder="搜索姓名、护照号、邮箱、参考编号..."
          clearable
          style="width: 300px"
          @input="handleSearch"
        >
          <template #prefix>
            <el-icon><Search /></el-icon>
          </template>
        </el-input>

        <el-button @click="loadAppointments">
          <el-icon><Refresh /></el-icon>
          刷新
        </el-button>
        <div class="remote-box">
          <el-switch v-model="ui.backgroundCancel" active-text="后台取消" inactive-text="前台取消" />
          <el-button @click="openQueryDialog">
            查询/更改/取消预约
          </el-button>
        </div>
      </div>
    </div>

    <!-- 过滤器 -->
    <div class="filters">
      <!-- 基础筛选：状态 / 预约日期 / 成功日期 -->
      <el-form :model="filters" inline class="filters-form">
        <el-form-item label="预约状态">
          <el-select v-model="filters.appointment_status" multiple collapse-tags collapse-tags-tooltip placeholder="选择状态" clearable>
            <el-option label="待处理" value="pending" />
            <el-option label="已预约" value="booked" />
            <el-option label="已确认" value="confirmed" />
            <el-option label="已取消" value="cancelled" />
            <el-option label="失败" value="failed" />
          </el-select>
        </el-form-item>

        <el-form-item label="预约日期">
          <el-date-picker
            v-model="appointmentDateRange"
            type="daterange"
            value-format="YYYY-MM-DD"
            :shortcuts="appointmentDateShortcuts"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            unlink-panels
          />
        </el-form-item>

        <el-form-item label="预约成功时间">
          <el-date-picker
            v-model="bookedAtRange"
            type="datetimerange"
            value-format="YYYY-MM-DD HH:mm:ss"
            format="YYYY-MM-DD HH:mm:ss"
            :shortcuts="bookedAtShortcuts"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            unlink-panels
            style="width: 380px"
          />
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="applyFilters">
            筛选
          </el-button>
          <el-button @click="resetFilters">
            重置
          </el-button>
          <el-button text @click="showAdvancedFilters = !showAdvancedFilters">
            {{ showAdvancedFilters ? '收起高级' : '展开高级' }}
          </el-button>
        </el-form-item>
      </el-form>

      <!-- 高级筛选：签发国家 / 数据类型 / 监控类型 / 优先级 / 创建时间 -->
      <transition name="el-fade-in">
        <div v-show="showAdvancedFilters" class="mt-3">
          <el-form :model="filters" inline class="filters-form">
            <el-form-item label="签发国家">
              <el-select v-model="filters.issuing_country" multiple collapse-tags collapse-tags-tooltip placeholder="选择国家" clearable>
                <el-option label="中国内地" value="CN" />
                <el-option label="海外" value="OTH" />
              </el-select>
            </el-form-item>

            <el-form-item label="数据类型">
              <el-select v-model="filters.is_placeholder" placeholder="选择类型" clearable>
                <el-option label="真实数据" :value="false" />
                <el-option label="占号数据" :value="true" />
              </el-select>
            </el-form-item>

            <el-form-item label="监控类型">
              <el-select v-model="filters.is_monitor" placeholder="全部/非监控/监控" clearable>
                <el-option label="非监控" :value="false" />
                <el-option label="监控" :value="true" />
              </el-select>
            </el-form-item>

            <el-form-item label="优先级">
              <el-select v-model="filters.priority" multiple collapse-tags collapse-tags-tooltip placeholder="选择优先级" clearable>
                <el-option label="1级" :value="1" />
                <el-option label="2级" :value="2" />
                <el-option label="3级" :value="3" />
                <el-option label="4级" :value="4" />
                <el-option label="5级" :value="5" />
              </el-select>
            </el-form-item>

            <el-form-item label="创建时间">
              <el-date-picker
                v-model="createdAtRange"
                type="daterange"
                value-format="YYYY-MM-DD"
                :shortcuts="createdAtShortcuts"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                unlink-panels
              />
            </el-form-item>
          </el-form>
        </div>
      </transition>
    </div>

    <!-- 数据表格 -->
    <div class="table-container">
      <div class="table-flex">
        <el-table
          v-loading="loading"
          :data="appointments"
          stripe
          border
          height="100%"
          @selection-change="handleSelectionChange"
          @sort-change="handleSortChange"
        >
          <el-table-column type="selection" width="55" />

          <el-table-column prop="sequence_number" label="序号" width="120" sortable="custom">
            <template #default="{ row }">
              <el-tag v-if="row.is_placeholder" type="warning" size="small">
                占号
              </el-tag>
              <el-tag v-if="row.is_monitor" type="info" size="small" class="ml-1">
                监控
              </el-tag>
              {{ row.sequence_number }}
            </template>
          </el-table-column>

          <el-table-column prop="full_name" label="姓名" width="100" />

          <el-table-column prop="passport_number" label="护照号码" width="120" />

          <el-table-column prop="license_number" label="驾照号码" width="150" show-overflow-tooltip />

          <el-table-column prop="email" label="邮箱" width="200" show-overflow-tooltip />

          <el-table-column prop="phone_number" label="电话" width="120" />

          <el-table-column label="签发信息" width="120">
            <template #default="{ row }">
              <div class="text-sm">
                <div>{{ row.issuing_country === 'CN' ? '中国内地' : '海外' }}</div>
                <div class="text-gray-500">
                  {{ row.issuing_authority === 'GD' ? '广东省' : '其他' }}
                </div>
              </div>
            </template>
          </el-table-column>

          <el-table-column prop="priority" label="优先级" width="80" align="center">
            <template #default="{ row }">
              <el-tag :type="getPriorityType(row.priority)" size="small">
                {{ row.priority }}级
              </el-tag>
            </template>
          </el-table-column>

          <el-table-column prop="appointment_status" label="状态" width="100" align="center">
            <template #default="{ row }">
              <el-tag :type="getStatusType(row.appointment_status)" size="small">
                {{ getStatusText(row.appointment_status) }}
              </el-tag>
            </template>
          </el-table-column>

          <el-table-column prop="appointment_date" label="预约日期" width="120" sortable="custom">
            <template #default="{ row }">
              <span v-if="row.appointment_date">{{ formatAppointmentDate(row.appointment_date) }}</span>
              <span v-else class="text-gray-400">未预约</span>
            </template>
          </el-table-column>

          <el-table-column prop="appointment_time" label="预约时间" width="100">
            <template #default="{ row }">
              <span v-if="row.appointment_time">{{ row.appointment_time }}</span>
              <span v-else class="text-gray-400">-</span>
            </template>
          </el-table-column>

          <el-table-column prop="reference_number" label="参考编号" width="160" show-overflow-tooltip>
            <template #default="{ row }">
              {{ row.reference_number || '-' }}
            </template>
          </el-table-column>

          <el-table-column prop="booked_at" label="预约成功时间" width="140" sortable="custom">
            <template #default="{ row }">
              {{ row.booked_at ? formatDateTime(row.booked_at) : '-' }}
            </template>
          </el-table-column>

          <el-table-column prop="created_at" label="创建时间" width="120" sortable="custom">
            <template #default="{ row }">
              {{ formatDateTime(row.created_at) }}
            </template>
          </el-table-column>

          <el-table-column prop="updated_at" label="更新时间" width="120" sortable="custom">
            <template #default="{ row }">
              {{ row.updated_at ? formatDateTime(row.updated_at) : '-' }}
            </template>
          </el-table-column>

          <el-table-column label="操作" width="200" fixed="right">
            <template #default="{ row }">
              <el-button size="small" @click="editAppointment(row)">
                编辑
              </el-button>
              <el-button size="small" type="info" @click="viewLogs(row)">
                日志
              </el-button>
              <el-button
                size="small"
                type="warning"
                :disabled="!row.reference_number || row.appointment_status === 'cancelled' || batchRefsCancelling[String(row.reference_number || '').slice(-6)] === true || batchCancelRunning"
                :loading="rowCancelling[row.id] === true || batchRefsCancelling[String(row.reference_number || '').slice(-6)] === true"
                @click="cancelAppointment(row)"
              >
                取消预约
              </el-button>
              <el-button
                size="small"
                type="danger"
                @click="deleteAppointment(row)"
              >
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <!-- 分页 -->
      <div class="pagination">
        <el-pagination
          v-model:current-page="pagination.page"
          v-model:page-size="pagination.page_size"
          :page-sizes="[20, 50, 100, 200]"
          :total="pagination.total"
          layout="total, sizes, prev, pager, next, jumper"
          @size-change="loadAppointments"
          @current-change="loadAppointments"
        />
      </div>
    </div>

    <!-- 清空所有记录功能已移除 -->

    <!-- 创建/编辑对话框 -->
    <AppointmentFormDialog
      v-model="showCreateDialog"
      :appointment="editingAppointment"
      @success="handleFormSuccess"
    />

    <!-- 操作日志对话框 -->
    <LogsDialog
      v-model="showLogsDialog"
      :appointment-id="currentAppointmentId"
    />

    <!-- 占号管理对话框 -->
    <PlaceholderManagementDialog
      v-model="showPlaceholderDialog"
      @success="onPlaceholderSuccess"
      @generating-change="onPlaceholderGeneratingChange"
    />

    <!-- 查询预约对话框 -->
    <el-dialog v-model="queryDialog.visible" title="查询/更改/取消预约" width="860px">
      <div class="mb-3 flex flex-wrap items-end items-center gap-3">
        <div class="flex items-center gap-2">
          <span class="text-gray-500">查询方式：</span>
          <el-radio-group v-model="queryDialog.method">
            <el-radio label="ref">
              参考编号
            </el-radio>
            <el-radio label="id">
              证件/执照
            </el-radio>
          </el-radio-group>
        </div>

        <template v-if="queryDialog.method === 'ref'">
          <el-input v-model="queryDialog.refLast6" placeholder="参考号后6位" clearable style="width: 160px" />
        </template>
        <template v-else>
          <el-input v-model="queryDialog.idQuery.identity" placeholder="身份证/护照" clearable style="width: 200px" />
          <el-input v-model="queryDialog.idQuery.license" placeholder="驾照号码" clearable style="width: 200px" />
        </template>

        <div class="flex items-center gap-2">
          <span class="text-gray-500">辅助填充：</span>
          <el-button :disabled="!selectedRows.length" @click="fillQueryFromSelected">
            用选中记录填充
          </el-button>
        </div>

        <el-button type="primary" :loading="queryDialog.running" @click="submitQueryDialog">
          查询
        </el-button>
      </div>

      <el-divider>更改预约（选中记录）</el-divider>
      <div class="mb-3 flex flex-wrap items-center gap-3">
        <el-input v-model="updateDialog.officeCode" placeholder="办事处(默认HKLO)" style="width: 160px" />
        <el-input v-model="updateDialog.preferredDates" placeholder="日期(YYYY-MM-DD,逗号分隔)" style="width: 260px" />
        <el-input v-model="updateDialog.preferredTimes" placeholder="时间(HH:MM,逗号分隔)" style="width: 260px" />
        <el-button type="warning" :loading="updateDialog.running" @click="submitUpdateDialog">
          提交更改
        </el-button>
      </div>

      <div v-if="currentQueryResult">
        <el-descriptions :column="2" border size="small">
          <el-descriptions-item label="参考后6位">
            {{ currentQueryResult.ref || queryDialog.refLast6 }}
          </el-descriptions-item>
          <el-descriptions-item label="结果">
            <el-tag :type="currentQueryResult.success ? 'success' : 'danger'">
              {{ currentQueryResult.success ? '成功' : '失败' }}
            </el-tag>
          </el-descriptions-item>

          <el-descriptions-item label="参考编号" span="2">
            {{ currentQueryResult?.data?.parsed?.reference_number || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="预约日期">
            {{ currentQueryResult?.data?.parsed?.appointment_date_dmy || currentQueryResult?.data?.parsed?.appointment_date || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="预约时间">
            {{ currentQueryResult?.data?.parsed?.appointment_time || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="办事处">
            {{ currentQueryResult?.data?.parsed?.office_display || currentQueryResult?.data?.parsed?.office || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="服务类型">
            {{ currentQueryResult?.data?.parsed?.service_type_text || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="姓名">
            {{ currentQueryResult?.data?.parsed?.applicant_name_masked || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="证件号">
            {{ currentQueryResult?.data?.parsed?.id_number_masked || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="电话">
            {{ currentQueryResult?.data?.parsed?.phone_masked || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="邮箱">
            {{ currentQueryResult?.data?.parsed?.email_masked || '-' }}
          </el-descriptions-item>
        </el-descriptions>

        <div class="mt-3 flex gap-8">
          <div class="flex gap-8">
            <el-button size="small" type="primary" :disabled="!selectedRows.length" @click="writebackFromQueryRow(currentQueryResult)">
              写回选中
            </el-button>
            <el-button
              size="small"
              type="danger"
              :disabled="!currentQueryResult?.data?.parsed?.reference_number"
              :loading="queryDialog.running"
              @click="cancelFromQueryRow(currentQueryResult)"
            >
              取消预约
            </el-button>
          </div>
        </div>

        <el-collapse class="mt-3">
          <el-collapse-item title="原始数据" name="raw">
            <pre style="white-space: pre-wrap; word-break: break-all; max-height: 240px; overflow: auto;">{{ JSON.stringify(currentQueryResult.data || { error: currentQueryResult.error }, null, 2) }}</pre>
          </el-collapse-item>
        </el-collapse>
      </div>
      <div v-else class="text-gray-500">
        无结果
      </div>
      <template #footer>
        <el-button @click="queryDialog.visible = false">
          关闭
        </el-button>
      </template>
    </el-dialog>

    <!-- 原取消预约对话框已并入查询弹窗 -->

    <!-- 更改预约表单对话框（选中） 已并入查询弹窗上方区块 -->

    <!-- 修复数据对话框 -->
    <el-dialog v-model="repairDialog.visible" title="修复缺失的预约数据" width="600px">
      <div class="mb-4">
        <el-alert
          type="info"
          show-icon
          :closable="false"
        >
          <template #title>
            <div>
              <p>此功能将查找所有状态为"已预约"但缺少参考编号、预约日期或预约时间的记录。</p>
              <p class="mt-2">
                修复策略：
              </p>
              <ul class="ml-4 mt-1 list-disc">
                <li>优先通过参考编号查询（如果存在）</li>
                <li>如果没有参考编号，则通过证件号/执照号查询</li>
              </ul>
            </div>
          </template>
        </el-alert>
      </div>

      <div v-if="repairDialog.result && repairDialog.result.total !== undefined" class="mb-4">
        <el-alert
          :type="repairDialog.result.failed > 0 ? 'warning' : 'success'"
          show-icon
          :closable="false"
        >
          <template #title>
            <div>
              <p class="font-bold">
                {{ repairDialog.result.message }}
              </p>
              <div class="mt-2 text-sm">
                <p>总计：{{ repairDialog.result.total }} 条</p>
                <p>成功修复：{{ repairDialog.result.repaired }} 条</p>
                <p>跳过：{{ repairDialog.result.skipped }} 条</p>
                <p>失败：{{ repairDialog.result.failed }} 条</p>
              </div>
            </div>
          </template>
        </el-alert>
      </div>

      <template #footer>
        <el-button @click="repairDialog.visible = false">
          关闭
        </el-button>
        <el-button type="primary" :loading="repairDialog.running" @click="handleRepairData">
          开始修复
        </el-button>
      </template>
    </el-dialog>

    <!-- 导入结果详情对话框 -->
    <el-dialog v-model="importDialog.visible" title="导入结果详情" width="900px">
      <el-alert
        v-if="importDialog.errors?.length"
        :title="`共有 ${importDialog.errors.length} 条失败记录，请根据错误提示修正后重试。`"
        type="warning"
        show-icon
        class="mb-3"
      />
      <el-table :data="importDialog.errors" border height="420">
        <el-table-column prop="row" label="行号" width="100" />
        <el-table-column prop="message" label="错误信息" width="260" />
        <el-table-column label="原始数据">
          <template #default="{ row }">
            <pre style="white-space: pre-wrap; word-break: break-all; max-height: 360px; overflow: auto;">
{{ JSON.stringify(row.data || {}, null, 2) }}
            </pre>
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button @click="importDialog.visible = false">
          关闭
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.appointment-page {
  padding: 20px;
  background: #f5f5f5;
  /* 让页面在主容器内滚动时自适应高度 */
  min-height: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  /* 在小窗口下允许滚动，避免表格区域被完全挤压看不见 */
  overflow: auto;
}

.page-header {
  margin-bottom: 20px;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  margin-bottom: 20px;
}

.stat-card {
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  display: flex;
  align-items: center;
  gap: 16px;
}

.stat-icon {
  width: 48px;
  height: 48px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
}

.stat-number {
  font-size: 24px;
  font-weight: bold;
  margin: 0;
}

.stat-label {
  color: #666;
  margin: 4px 0 0 0;
  font-size: 14px;
}

.toolbar {
  background: white;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  margin-bottom: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.toolbar-left {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.toolbar-right {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.remote-box { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

@media (max-width: 1200px) {
  .stats-grid {
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  }
}

@media (max-width: 768px) {
  .toolbar {
    flex-direction: column;
    align-items: stretch;
  }
  .toolbar-left,
  .toolbar-right {
    width: 100%;
    justify-content: flex-start;
  }
  .remote-box {
    gap: 6px;
  }
}

.filters {
  background: white;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  margin-bottom: 16px;
}

/* 让筛选区域的选择器宽度更明确，避免内容不明显 */
.filters :deep(.el-form-item) {
  margin-right: 16px;
}
.filters :deep(.el-select),
.filters :deep(.el-date-editor) {
  min-width: 220px;
}
.filters-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.table-container {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  overflow: hidden;
  /* 占据剩余空间，内部滚动交给表格 */
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* 窗口高度过小时，至少留出可见表格空间 */
  min-height: 320px;
}

.table-flex {
  flex: 1;
  min-height: 0;
  display: flex;
}

.pagination {
  padding: 16px;
  display: flex;
  justify-content: center;
}
</style>
